home *** CD-ROM | disk | FTP | other *** search
- /*
- * MSGLINK.C - Link QuickBBS reply chains using EID lines
- *
- * Msged/Q message editor for QuickBBS Copyright 1990 by P.J. Muller
- *
- * This file is not used by Msged/Q
- */
-
- #define SHOWMEM
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #ifdef SHOWMEM
- #include <alloc.h>
- #endif
-
- #define MAIN
- #include "msglink.h"
-
- typedef BYTE BITSET[32]; /* [0..255] */
-
- #define clearset(a) memset(a,0,sizeof(BITSET));
- #define setbit(b,a) a[(b)/8] |= (1 << ((b) % 8))
- #define isset(b,a) (a[(b)/8] & (1 << ((b) % 8)))
-
- typedef struct {
- int num; /* board number */
- char *name; /* board name */
- } BOARD;
-
- static BOOLEAN readeid(MSGHEADER *hdr, ID *EID, ID *RID);
- static int readlinks(BITSET boards, MSGDATA ***link);
- #ifdef DEBUG
- static void showlist(MSGDATA **link, int len);
- #endif
- static void linklist(MSGDATA **link, int LEN);
- static int sort_st_crc(MSGDATA **a, MSGDATA **b);
- static char *prepsubj(char *s);
- static void dosubjs(MSGDATA **link, int len);
- static int sort_num(MSGDATA **a, MSGDATA **b);
- static void writelinks(MSGDATA **link,int len);
- static void parseargs(int argc,char *argv[],BITSET useboard,BOOLEAN *subj,BOOLEAN *eid);
- static BOARD **readareas(char *fname);
- static BOOLEAN readtosslog(char *tosslog, BOARD **board, BITSET useboard);
-
- static BOOLEAN subjlink; /* link Subj lines */
- static BOOLEAN eidlink; /* link EID lines */
-
- #define fatal(s) { fprintf(stderr,"\nFatal: %s\n",s); exit(1); }
-
- int main(int argc, char *argv[])
- {
- MSGDATA **link;
- int len;
- #ifdef SHOWMEM
- long memory;
- #endif
- BITSET useboard;
-
- fprintf(stderr,"EID message linking Version 1.1 Copyright 1991 P.J. Muller 5:7102/11\n\n");
-
- parseargs(argc,argv,useboard,&subjlink,&eidlink);
-
- if (!openmsgbase())
- fatal("Could not open message base");
-
- #ifdef SHOWMEM
- memory = coreleft();
- #endif
- len = readlinks(useboard, &link);
- if (len != 0) {
- #ifdef SHOWMEM
- fprintf(stderr,"\n\tUsing %dk memory",(int)((memory-coreleft())/1024));
- #endif
-
- fprintf(stderr,"\n\nLinking messages...");
- if (subjlink)
- dosubjs(link,len);
-
- linklist(link,len); /* after this link will be sortde by number */
-
- fprintf(stderr,"\n\nWriting new links...");
- writelinks(link,len);
- fprintf(stderr,"\n\tDone\n");
- } else {
- fprintf(stderr,"Less than two messages selected -> nothing to do!\n");
- } /* else */
-
- if (!closemsgbase())
- fatal("Could not close message base");
-
- return(0);
- } /* main */
-
- /*
- * Compare function for sort by time/date stamp then CRC then board number
- */
-
- static int sort_st_crc(MSGDATA **a, MSGDATA **b)
- {
- int diff;
-
- if ((*a)->EID.st < (*b)->EID.st)
- return(-1); /* a < b */
- if ((*a)->EID.st > (*b)->EID.st)
- return(1); /* a > b */
-
- /* time/date stamps are equal */
-
- /* can do this trick because crc is an int */
- diff = (*a)->EID.crc - (*b)->EID.crc;
- if (diff != 0)
- return(diff);
- /* cast to int for trick */
- return((int)(*a)->area - (int)(*b)->area);
- } /* sort_st_crc */
-
- /*
- * Compare function for sort by area then subj then number
- */
-
- static int sort_subj(MSGDATA **a, MSGDATA **b)
- {
- int diff;
-
- diff = (int)(*a)->area - (int)(*b)->area;
- if (diff != 0)
- return(diff);
-
- diff = strcmp((*a)->subj,(*b)->subj);
- if (diff != 0)
- return(diff);
-
- return((*a)->no - (*b)->no);
- #ifdef ZAPTHISOUT
- if ((*a)->EID.st < (*b)->EID.st)
- return(-1); /* a < b */
- if ((*a)->EID.st > (*b)->EID.st)
- return(1); /* a > b */
-
- return(0);
- #endif
- } /* sort_subj */
-
- /*
- * Compare function for sort by 'no'
- */
-
- static int sort_num(MSGDATA **a, MSGDATA **b)
- {
- return((*a)->no - (*b)->no);
- } /* sort_num */
-
- /*
- * Display the list of messages
- */
-
- #ifdef DEBUG
- static void showlist(MSGDATA **link, int len)
- {
- printf("\n");
- while (len--) {
- printf("%4d %04x %08lx %04x %08lx %4d %4d %4d %4d %s\n", (*link)->no,
- (*link)->EID.crc,(*link)->EID.st,
- (*link)->x.RID.crc,(*link)->x.RID.st,
- (*link)->up,(*link)->down,
- (*link)->oup,(*link)->odown,
- (*link)->subj == NULL ? "" : (*link)->subj);
- link++;
- } /* while */
- printf("\n");
- } /* showlist */
- #endif
-
- /*
- * Link the messages
- */
-
- static void linklist(MSGDATA **numidx, int LEN)
- {
- int c;
- size_t len = LEN; /* lfind wants a pointer to size_t */
- MSGDATA key,*keyptr,**dateidx;
- MSGDATA **found;
-
- fprintf(stderr,"\n\tSorting EID's");
-
- dateidx = calloc(len,sizeof(MSGDATA *)); /* make another index */
- if (dateidx == NULL)
- fatal("Out of memory.");
- memcpy(dateidx,numidx,len*sizeof(MSGDATA *));
- qsort(dateidx,len,sizeof(MSGDATA *),sort_st_crc);
- qsort(numidx,len,sizeof(MSGDATA *),sort_num);
- #ifdef DEBUG
- /*showlist(numidx,len);*/
- #endif
-
- fprintf(stderr,"\n\tLinking EID's");
- memset(&key,0,sizeof(key));
- keyptr = &key;
- for (c = 0; c < len; c++) {
- if (numidx[c]->x.RID.st == 0)
- continue; /* no RID */
- key.area = numidx[c]->area;
- key.EID = numidx[c]->x.RID;
- found = bsearch(&keyptr,dateidx,len,sizeof(dateidx[0]),sort_st_crc);
- /* step to end of reply list */
- while ((found != NULL) && (key.no = (*found)->up) != 0)
- found = bsearch(&keyptr,numidx,len,sizeof(numidx[0]),sort_num);
-
- if (found != NULL) { /* add link */
- if ((*found)->no < numidx[c]->no) { /* avoid endless loops */
- (*found)->up = numidx[c]->no;
- numidx[c]->down = (*found)->no;
- } /* if */
- } /* if */
-
- } /* for */
-
- #ifdef DEBUG
- /* showlist(numidx,len); */
- #endif
-
- } /* linklist */
-
- /*
- * Link the Subj lines
- */
-
- static void dosubjs(MSGDATA **link, int len)
- {
- MSGDATA **step, **base=link;
- int c;
-
- fprintf(stderr,"\n\tSorting Subjects");
- qsort(link,len,sizeof(link[0]),sort_subj);
-
- fprintf(stderr,"\n\tLinking Subjects");
- c = len;
- while (c > 1) {
- step = base+1; /* Subj after current */
- if ((*base)->EID.st != 0) /* skip empty EID's */
- while ((--c > 0) && ((*base)->area == (*step)->area) &&
- (strcmp((*base)->subj,(*step)->subj)) == 0) {
- (*step)->x.RID = (*base)->EID; /* create RID */
- ++step;
- } /* while */
- base = step; /* skip same subj's */
- } /* while */
- #ifdef DEBUG
- /* showlist(link,len); */
- #endif
-
- #ifndef DEBUG /* free()'ing not really necessary */
- for (c = 0, step = link; c < len; c++, step++) {
- free((*step)->subj);
- (*step)->subj = NULL;
- } /* for */
- #endif
-
- } /* dosubjs */
-
- /*
- * Read all the links of a message base
- * Return an array of pointers to MSGDATA instances
- */
-
- static int readlinks(BITSET boards, MSGDATA ***link)
- {
- MSGHEADER header;
- MSGDATA *temp;
- int board, idx, len, maxlen;
- ID EID,RID;
-
- maxlen = 0;
- for (idx=0; idx < filemsgs; idx++) {
- if ((msgidx[idx].msgnum == -1) || (msgidx[idx].board == 0))
- continue; /* deleted message */
-
- board = msgidx[idx].board; /* board number */
- if (!isset(board-1,boards))
- continue; /* we are not interested */
-
- maxlen++; /* one more */
- } /* for */
-
- if (maxlen < 2) return 0;
-
- fprintf(stderr,"Reading %d messages...",maxlen);
-
- *link = calloc(maxlen,sizeof(MSGDATA *)); /* allocate array */
- if (*link == NULL)
- fatal("Out of memory.");
-
- /*
- * Now read the message headers
- */
-
- fprintf(stderr,"\n\tReading Headers");
- len = 0; /* will now count real length */
- for (idx=0; idx < filemsgs; idx++) {
- if ((msgidx[idx].msgnum == -1) || (msgidx[idx].board == 0))
- continue; /* deleted message */
-
- board = msgidx[idx].board; /* board number */
- if (!isset(board-1,boards))
- continue; /* we are not interested */
-
- if (readheader(msgidx[idx].msgnum,&header)) {
- if (header.numrecs == 0) continue;
-
- if ((temp = calloc(1,sizeof(MSGDATA))) == NULL)
- fatal("Out of memory.");
-
- temp->no = header.msgnum;
- temp->area = board;
- temp->oup = header.up;
- temp->odown = header.reply;
- temp->EID.st = dostime(header.posttime,header.postdate,TRUE);
- temp->EID.crc = rand();
- temp->x.startrec = header.startrec;
- if (subjlink)
- temp->subj = prepsubj(header.subj);
-
- (*link)[len++] = temp; /* pointer to last */
- } /* if */
-
- } /* for */
-
- /*
- * By this time all messages will have a fake EID in memory
- * Now start reading real EID's and reply EID's if required
- */
-
- if (eidlink) {
- fprintf(stderr,"\n\tReading EID's");
- memset(&header,0,sizeof(header));
- for (idx=0; idx < len; idx++) {
- temp = (*link)[idx];
- header.numrecs = 1; /* read only 255 chars */
- header.startrec = temp->x.startrec; /* knows about the insides */
- header.msgnum = temp->no; /* of readtext() */
- if (!readeid(&header,&EID,&RID))
- fatal("Error while reading message text (memory?)");
- if (EID.st != 0) { /* found and EID, override default */
- temp->EID = EID;
- temp->x.RID = RID;
- } /* if */
- } /* for */
- } /* if */
-
- return(len);
- } /* readlinks */
-
- static BOOLEAN readeid(MSGHEADER *hdr, ID *EID, ID *RID)
- {
- char *text,*eid;
- int count;
-
- memset(EID,0,sizeof(ID));
- memset(RID,0,sizeof(ID));
-
- if ((text = readtext(hdr)) == NULL)
- return(FALSE);
-
- eid = strstr(text,"\01EID:");
- if (eid != NULL)
- count = sscanf(eid, "\01EID: %x %X %x %X\r", &EID->crc, &EID->st, &RID->crc, &RID->st);
-
- switch (count) { /* check for partial match */
- case 1:
- memset(EID,0,sizeof(ID));
- break;
- case 3:
- memset(RID,0,sizeof(ID));
- break;
- } /* switch */
-
- free(text);
-
- return(TRUE);
- } /* readeid */
-
- /*
- * Write all the new links of a message base
- */
-
- static void writelinks(MSGDATA **link,int len)
- {
- MSGHEADER header;
- MSGDATA **step;
- int c;
-
- #ifdef DEBUG
- /* showlist(link,len); */
- #endif
-
- fprintf(stderr,"\n\tWriting links");
- for (c = 0, step = link; c < len; c++, step++) {
- if (((*step)->up == (*step)->oup) && ((*step)->down == (*step)->odown))
- continue; /* no change to links */
- if (!readheader((*step)->no,&header))
- continue; /* can't read */
- header.up = (*step)->up;
- header.reply = (*step)->down;
- writeheader(&header);
- } /* for */
-
- } /* writelinks */
-
- /*
- * Prepare a Subj line and return a malloc'ed string
- */
-
- static char *prepsubj(char *s)
- {
- char subj[73];
-
- while (strnicmp(s,"Re: ",4) == 0)
- s += 4; /* skip leading RE:'s */
-
- strcpy(subj,s);
- subj[25] = EOS; /* at most 25 characters */
- strupr(subj);
-
- return(strdup(subj));
- } /* prepsubj */
-
- /*
- * Parse the command line
- */
-
- static void parseargs(int argc, char *argv[], BITSET useboard, BOOLEAN *subj, BOOLEAN *eid)
- {
- char *toss = NULL;
- BOOLEAN dirty = FALSE;
-
- clearset(useboard);
-
- if (argc < 2) {
- fprintf(stderr,"Usage: MsgLink [-s] [-e] [-llogname] [board] [board1-board2] ...\n");
- fprintf(stderr,"\nLinks QuickBBS messages in numerical order using ^aEID and Subject lines.\n");
- fprintf(stderr,"\n\t-e\tDisable ^aEID kludge line linking\n");
- fprintf(stderr, "\t-s\tDisable Subject line linking\n");
- fprintf(stderr, "\t-l\tSpecify a tossed board log file\n");
- fprintf(stderr,"\nE.g.:\tMsgLink 1-2 3-6 10 Links messages in boards 1-6 and 10\n");
- fprintf(stderr, "\tMsgLink -s 1-200 Links all messages using only EID's.\n");
- fprintf(stderr, "\tMsgLink -e 3 132 Links messages in board 3 & 132 using only Subj.\n");
- fprintf(stderr, "\tMsgLink -lQECHO.TMP Links messages in boards listed in QECHO.TMP\n");
- exit(1);
- } /* if */
-
- *subj = TRUE;
- *eid = TRUE;
-
- while (--argc) {
- ++argv;
- if (*argv[0] == '-') {
- switch (argv[0][1]) {
- case 's':
- case 'S':
- *subj = FALSE;
- break;
-
- case 'e':
- case 'E':
- *eid = FALSE;
- break;
-
- case 'l':
- case 'L':
- toss = argv[0]+2; /* skip '-L' */
- if (strlen(toss) == 0)
- fatal("Must specify filename directly after -L");
- break;
-
- default:
- fprintf(stderr,"Bad option '%c'\n",argv[0][1]);
- exit(1);
- } /* switch */
- } else {
- int count, a=0, b=0;
- count = sscanf(argv[0],"%d-%d",&a,&b);
- switch (count) {
- case 1:
- if ((a >= 1) && (a <= BLIM)) {
- setbit(a-1,useboard);
- dirty = TRUE;
- } else
- count = 0;
- break;
-
- case 2:
- if ((a >= 1) && (b <= BLIM)) {
- for (count=a-1; count < b; count++)
- setbit(count,useboard);
- dirty = TRUE;
- } else
- count = 0;
- break;
- } /* switch */
-
- if (count == 0) {
- fprintf(stderr,"Bad argument '%s'\n",argv[0]);
- exit(1);
- } /* if */
- } /* else */
- } /* while */
-
- /* Now read the tosslog and areas.bbs if specified */
- if (toss != NULL) {
- BOARD **board,**step;
- if ((board = readareas("areas.bbs")) != NULL) {
- dirty |= readtosslog(toss,board,useboard);
- for (step = board; *step != NULL; step++) {
- free(step[0]->name);
- free(step[0]);
- } /* for */
- free(board);
- board = NULL;
- } /* if */
- } /* if */
-
- if ((!dirty) || !(*subj || *eid)) {
- fprintf(stderr,"Nothing to do!\n");
- exit(1);
- } /* if */
- } /* parseargs */
-
- static BOARD **readareas(char *fname)
- {
- FILE *areas;
- char buffer[101];
- char bname[51];
- int match,num;
- BOARD **list;
- int sp = 0;
-
- list = calloc(BLIM+1,sizeof(list[0]));
-
- if ((areas = fopen(fname,"r")) == NULL) {
- fprintf(stderr,"Cannot open %s\n",fname);
- exit(1);
- } /* if */
-
- while (fgets(buffer,100,areas) != NULL) {
- match = sscanf(buffer,"%d %50s",&num,bname);
- if (match == 2) {
- list[sp] = calloc(1,sizeof(*list[0]));
- list[sp]->num = num;
- list[sp]->name = strdup(bname);
- sp++;
- } /* if */
- } /* while */
-
- list[sp] = NULL;
- list = realloc(list,(sp+1)*sizeof(list[0]));
-
- fclose(areas);
-
- return(list);
- } /* readareas */
-
- static BOOLEAN readtosslog(char *tosslog, BOARD **board, BITSET useboard)
- {
- FILE *log;
- BOARD **look;
- char name[51];
- BOOLEAN dirty = FALSE;
-
- if (board == NULL)
- return(FALSE);
-
- if ((log = fopen(tosslog,"r")) == NULL) {
- fprintf(stderr,"Could not open %s\n",tosslog);
- exit(1);
- } /* if */
-
- while (fscanf(log,"%50s",&name) > 0) {
- for (look = board; look[0] != NULL; look++)
- if (stricmp(look[0]->name,name) == 0)
- break;
- if (*look != NULL) {
- setbit(look[0]->num-1,useboard); /* select that board */
- dirty = TRUE;
- } /* if */
- } /* while */
-
- fclose(log);
-
- return(dirty);
- } /* readtosslog */